import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import OpenApi, * as $OpenApi from '@alicloud/openapi-client';
import polardb20170801, * as $polardb20170801 from '@alicloud/polardb20170801';
import Cms20190101, * as $Cms20190101 from '@alicloud/cms20190101';
import R_kvstore20150101, * as $R_kvstore20150101 from '@alicloud/r-kvstore20150101';
import { PluginAliyunMonitorMetricDto } from './plugin-aliyun-monitor-metric.dto';
import { Server } from '../../server/server.entity';
import moment = require('moment');
import { PluginAliyunSetting, PluginAliyunSettingConfigKeys } from './plugin-aliyun-setting.entity';
import { PluginAliyunSLB } from './plugin-aliyun-slb.entity';
import { PluginAliyunMonitorSLB_QPS } from './plugin-aliyun-monitor-slb-qps.entity';
import { PluginAliyunMonitorPolardbMetric } from './plugin-aliyun-monitor-polardb-metric.entity';
import { PluginAliyunPolardb } from './plugin-aliyun-polardb.entity';
import { PluginAliyunRedis } from './plugin-aliyun-redis.entity';
import { PluginAliyunMonitorRedisMetric } from './plugin-aliyun-monitor-redis-metric.entity';
import { PluginAliyunRocketmq } from './plugin-aliyun-rocketmq.entity';
import {
  PluginAliyunMonitorRocketmqMetric,
  PluginAliyunMonitorRocketmqMetricType,
} from './plugin-aliyun-monitor-rocketmq-metric.entity';
import {
  PluginAliyunMonitorEcsMetric,
  PluginAliyunMonitorEcsMetricName,
} from './plugin-aliyun-monitor-ecs-metric.entity';


/**
 * 阿里云监控任务调度
 */
@Injectable()
export default class PluginAliyunMonitorTaskService {

  private readonly logger = new Logger(PluginAliyunMonitorTaskService.name);

  constructor(
    @InjectRepository(PluginAliyunMonitorEcsMetric)
    private pluginAliyunMonitorEcsMetricRepository: Repository<PluginAliyunMonitorEcsMetric>,
    @InjectRepository(Server)
    private serverRepository: Repository<Server>,
    @InjectRepository(PluginAliyunSetting)
    private aliyunSettingRepository: Repository<PluginAliyunSetting>,
    @InjectRepository(PluginAliyunSLB)
    private pluginAliyunSLBRepository: Repository<PluginAliyunSLB>,
    @InjectRepository(PluginAliyunMonitorSLB_QPS)
    private pluginAliyunMonitorSLBQPSRepository: Repository<PluginAliyunMonitorSLB_QPS>,
    @InjectRepository(PluginAliyunPolardb)
    private pluginAliyunPolardbRepository: Repository<PluginAliyunPolardb>,
    @InjectRepository(PluginAliyunMonitorPolardbMetric)
    private pluginAliyunMonitorPolardbQpsRepository: Repository<PluginAliyunMonitorPolardbMetric>,
    @InjectRepository(PluginAliyunRedis)
    private pluginAliyunRedisRepository: Repository<PluginAliyunRedis>,
    @InjectRepository(PluginAliyunMonitorRedisMetric)
    private pluginAliyunMonitorRedisMetricRepository: Repository<PluginAliyunMonitorRedisMetric>,
    @InjectRepository(PluginAliyunRocketmq)
    private pluginAliyunRocketmqRepository: Repository<PluginAliyunRocketmq>,
    @InjectRepository(PluginAliyunMonitorRocketmqMetric)
    private pluginAliyunMonitorRocketmqMetricRepository: Repository<PluginAliyunMonitorRocketmqMetric>,
  ) {

  }

  @Cron('0 */15 * * * *')//  1分钟执行一次，获取前10分钟到当前时间区间数据
  async handleCron() {
    const accessKeyId = await this.aliyunSettingRepository.findOne({ key: PluginAliyunSettingConfigKeys.accessKeyId });
    const accessKeySecret = await this.aliyunSettingRepository.findOne({ key: PluginAliyunSettingConfigKeys.accessKeySecret });

    if (!accessKeyId || !accessKeySecret) {
      console.log('请先配置阿里云子账号 accessKey Id and Secret');
      return;
    }
    const config = new $OpenApi.Config({
      // 您的AccessKey ID
      accessKeyId: accessKeyId.value,
      // 您的AccessKey Secret
      accessKeySecret: accessKeySecret.value,
    });


    const before20Min = 60 * 60 * 1000;

    // 访问的域名
    config.endpoint = 'metrics.cn-shenzhen.aliyuncs.com';
    const client = new Cms20190101(config);
    const current = new Date().getTime();
    // 当前时间的前60分钟作为开始时间
    const startTime = moment(current - before20Min).format('YYYY-MM-DD HH:mm:ss');
    // 当前时间作为结束时间
    const endTime = moment(current).format('YYYY-MM-DD HH:mm:ss');

    // ECS ----------------------- start ---------------------------
    const serverList: Server[] = await this.serverRepository.find({ isMonitor: true });
    for (const server of serverList) {
      const describeMetricDataRequest = new $Cms20190101.DescribeMetricDataRequest({
        namespace: 'acs_ecs_dashboard',
        metricName: 'cpu_total',
        period: '15',
        dimensions: `[{'instanceId': '${server.instanceId}'}]`,
        startTime: startTime,
        endTime: endTime,
      });

      // cpu 使用率
      describeMetricDataRequest.metricName = PluginAliyunMonitorEcsMetricName.cpu_total;
      await this.saveMetricData(describeMetricDataRequest, {
        instanceId: server.instanceId,
        instanceName: server.name,
      }, this.pluginAliyunMonitorEcsMetricRepository, client);
      // 内存 使用率
      describeMetricDataRequest.metricName = PluginAliyunMonitorEcsMetricName.memory_usedutilization;
      await this.saveMetricData(describeMetricDataRequest, {
        instanceId: server.instanceId,
        instanceName: server.name,
      }, this.pluginAliyunMonitorEcsMetricRepository, client);
      // cpu 1m负载
      describeMetricDataRequest.metricName = PluginAliyunMonitorEcsMetricName.cpu_load_1m;
      await this.saveMetricData(describeMetricDataRequest, {
        instanceId: server.instanceId,
        instanceName: server.name,
      }, this.pluginAliyunMonitorEcsMetricRepository, client);
      // cpu 5m负载
      describeMetricDataRequest.metricName = PluginAliyunMonitorEcsMetricName.cpu_load_5m;
      await this.saveMetricData(describeMetricDataRequest, {
        instanceId: server.instanceId,
        instanceName: server.name,
      }, this.pluginAliyunMonitorEcsMetricRepository, client);
      // cpu 15m负载
      describeMetricDataRequest.metricName = PluginAliyunMonitorEcsMetricName.cpu_load_15m;
      await this.saveMetricData(describeMetricDataRequest, {
        instanceId: server.instanceId,
        instanceName: server.name,
      }, this.pluginAliyunMonitorEcsMetricRepository, client);
    }
    // ECS ----------------------- end ---------------------------

    // SLB ----------------------- start ---------------------------
    const slbList = await this.pluginAliyunSLBRepository.find({ isMonitor: true });
    for (const slb of slbList) {
      const describeMetricDataRequest = new $Cms20190101.DescribeMetricDataRequest({
        namespace: 'acs_slb_dashboard',
        metricName: 'InstanceQps',
        period: '60',
        dimensions: `[{'instanceId': '${slb.instanceId}'}]`,
        startTime: startTime,
        endTime: endTime,
      });

      describeMetricDataRequest.metricName = 'InstanceQps';
      await this.saveMetricData(describeMetricDataRequest, {
        instanceId: slb.instanceId,
        instanceName: slb.instanceName,
      }, this.pluginAliyunMonitorSLBQPSRepository, client);
    }
    // SLB ----------------------- end ---------------------------

    // Rocketmq ----------------------- start ---------------------------
    const rocketmqList = await this.pluginAliyunRocketmqRepository.find({ isMonitor: true });
    for (const rocketmq of rocketmqList) {
      const describeMetricDataRequest = new $Cms20190101.DescribeMetricDataRequest({
        namespace: 'acs_rocketmq',
        period: '60',
        dimensions: `[{'instanceId': '${rocketmq.instanceId}'}]`,
        startTime: startTime,
        endTime: endTime,
      });

      // 消息堆积
      describeMetricDataRequest.metricName = PluginAliyunMonitorRocketmqMetricType.ConsumerLag;
      const groupIdList = JSON.parse(rocketmq.groupIdList);
      for (const groupId of groupIdList) {
        describeMetricDataRequest.dimensions = `[{'instanceId': '${rocketmq.instanceId}','groupId':'${groupId}'}]`;
        await this.saveMetricData(describeMetricDataRequest, {
          instanceId: rocketmq.instanceId,
          instanceName: rocketmq.instanceName,
          groupId: groupId,
        }, this.pluginAliyunMonitorRocketmqMetricRepository, client);

      }
      // 实例（Instance）每分钟接收消息数的数量
      describeMetricDataRequest.metricName = PluginAliyunMonitorRocketmqMetricType.ReceiveMessageCountPerInstance;
      await this.saveMetricData(describeMetricDataRequest, {
        instanceId: rocketmq.instanceId,
        instanceName: rocketmq.instanceName,
      }, this.pluginAliyunMonitorRocketmqMetricRepository, client);

      // 实例（Instance）每分钟发送消息数量
      describeMetricDataRequest.metricName = PluginAliyunMonitorRocketmqMetricType.SendMessageCountPerInstance;
      await this.saveMetricData(describeMetricDataRequest, {
        instanceId: rocketmq.instanceId,
        instanceName: rocketmq.instanceName,
      }, this.pluginAliyunMonitorRocketmqMetricRepository, client);

    }
    // Rocketmq ----------------------- end ---------------------------


    // Polardb ----------------------- start ---------------------------
    const polardbConfig = new $OpenApi.Config({
      // 您的AccessKey ID
      accessKeyId: accessKeyId.value,
      // 您的AccessKey Secret
      accessKeySecret: accessKeySecret.value,
    });
    // 访问的域名
    polardbConfig.endpoint = 'polardb.aliyuncs.com';
    const polardbClient = new polardb20170801(polardbConfig);

    const aliyunPolardbList = await this.pluginAliyunPolardbRepository.find({ isMonitor: true });
    const polardbStartTime = moment(current - before20Min).utc().format('YYYY-MM-DDTHH:mm');
    const polardbEndTime = moment(current).utc().format('YYYY-MM-DDTHH:mm');
    for (const polardb of aliyunPolardbList) {
      const describeDBClusterPerformanceRequest = new $polardb20170801.DescribeDBClusterPerformanceRequest({
        DBClusterId: `${polardb.instanceId}`,
        startTime: `${polardbStartTime}Z`,
        endTime: `${polardbEndTime}Z`,
      });
      describeDBClusterPerformanceRequest.key = 'PolarDBQPSTPS';
      await this.savePolardbMetricData(describeDBClusterPerformanceRequest, polardb, polardbClient);

      describeDBClusterPerformanceRequest.key = 'PolarDBCPU';
      await this.savePolardbMetricData(describeDBClusterPerformanceRequest, polardb, polardbClient);
      describeDBClusterPerformanceRequest.key = 'PolarDBMemory';
      await this.savePolardbMetricData(describeDBClusterPerformanceRequest, polardb, polardbClient);

    }
    // Polardb ----------------------- end ---------------------------

    // Redis ----------------------- start ---------------------------
    const redisConfig = new $OpenApi.Config({
      // 您的AccessKey ID
      accessKeyId: accessKeyId.value,
      // 您的AccessKey Secret
      accessKeySecret: accessKeySecret.value,
    });
    redisConfig.endpoint = 'r-kvstore.aliyuncs.com';
    const redisClient = new R_kvstore20150101(redisConfig);
    const redisStartTime = moment(current - before20Min).utc().format('YYYY-MM-DDTHH:mm:ss');
    const redisEndTime = moment(current).utc().format('YYYY-MM-DDTHH:mm:ss');
    const aliyunRedisList = await this.pluginAliyunRedisRepository.find({ isMonitor: true });
    for (const redis of aliyunRedisList) {
      const describeHistoryMonitorValuesRequest = new $R_kvstore20150101.DescribeHistoryMonitorValuesRequest({
        instanceId: `${redis.instanceId}`,
        startTime: `${redisStartTime}Z`,
        endTime: `${redisEndTime}Z`,
        intervalForHistory: '01m',
      });
      describeHistoryMonitorValuesRequest.monitorKeys = 'CpuUsage$db,MemoryUsage$db,UsedQPS$db';
      const redisResult = await redisClient.describeHistoryMonitorValues(describeHistoryMonitorValuesRequest).then(res => res.body);
      const redisMonitorHistory = JSON.parse(redisResult.monitorHistory);
      Object.keys(redisMonitorHistory).map(value => {

        const t = Date.parse(value);
        Object.keys(redisMonitorHistory[value]).map(vk => {
          const redisMetric = new PluginAliyunMonitorRedisMetric();
          try {
            redisMetric.id = redis.instanceId + vk + t;
            redisMetric.instanceId = redis.instanceId;
            redisMetric.average = redisMonitorHistory[value][vk];
            redisMetric.instanceName = redis.instanceName;
            redisMetric.t = t;
            redisMetric.type = vk;

            this.pluginAliyunMonitorRedisMetricRepository.save(redisMetric);
          } catch (e) {
            console.log('redisMetric', redisMetric);
          }


        });
      });
    }
    // Redis ----------------------- end ---------------------------


  }

  /**
   * 保存监控信息到对应实体中
   * @param describeMetricDataRequest
   * @param instanceInfo 实例信息
   * @param repository
   * @param client
   */
  async saveMetricData(describeMetricDataRequest: $Cms20190101.DescribeMetricDataRequest, instanceInfo: { instanceId: string, instanceName: string, groupId?: string }, repository: Repository<any>, client: Cms20190101) {
    const result = await client.describeMetricData(describeMetricDataRequest).then(res => res.body);
    if (result.code === '200') {
      const pluginMonitorServerMetricResponseList = JSON.parse(result.datapoints) as PluginAliyunMonitorMetricDto[];
      for (const pluginMonitorServerMetricRespons of pluginMonitorServerMetricResponseList) {
        const metric = {} as { serverId: number, serverName: string, instanceId: string, instanceName: string, t: number, maximum: number, average: number, minimum: number, id: string, metric: string, type: string, vip: string, groupId: string };
        metric.instanceId = instanceInfo.instanceId;
        metric.instanceName = instanceInfo.instanceName;
        metric.t = pluginMonitorServerMetricRespons.timestamp;
        metric.id = `${instanceInfo.instanceId}${pluginMonitorServerMetricRespons.timestamp}`;

        // ECS
        if (describeMetricDataRequest.namespace === 'acs_ecs_dashboard') {
          metric.maximum = pluginMonitorServerMetricRespons.Maximum;
          metric.average = pluginMonitorServerMetricRespons.Average;
          metric.minimum = pluginMonitorServerMetricRespons.Minimum;
          metric.metric = describeMetricDataRequest.metricName;
          // 阿里云服务器CPU负载
          if (describeMetricDataRequest.metricName.includes('load')) {
            metric.id = metric.id +pluginMonitorServerMetricRespons.timestamp+PluginAliyunMonitorEcsMetricName.cpu_load+ describeMetricDataRequest.metricName;
            metric.metric = PluginAliyunMonitorEcsMetricName.cpu_load;
            metric.type = describeMetricDataRequest.metricName;
          }
        }
        // SLB
        else if (describeMetricDataRequest.namespace === 'acs_slb_dashboard') {
          metric.average = pluginMonitorServerMetricRespons.Average;
          metric.vip = pluginMonitorServerMetricRespons.vip;
        }

        // Rocketmq
        else if (describeMetricDataRequest.namespace === 'acs_rocketmq') {
          metric.groupId = instanceInfo.groupId ? instanceInfo.groupId : describeMetricDataRequest.metricName;
          metric.type = describeMetricDataRequest.metricName;
          metric.id = `${instanceInfo.instanceId}${metric.type}${metric.groupId}${pluginMonitorServerMetricRespons.timestamp}`;
          metric.average = pluginMonitorServerMetricRespons.Sum;
        }

        // console.log('metric',metric);
        await repository.save(metric);
      }
    } else {
      console.log('describeMetricDataResponse', result);
    }

  }

  /**
   * 保存polardb监控数据
   * @param describeDBClusterPerformanceRequest
   * @param instanceInfo
   * @param client
   */
  async savePolardbMetricData(describeDBClusterPerformanceRequest: $polardb20170801.DescribeDBClusterPerformanceRequest, instanceInfo: { instanceId: string, instanceName: string }, client: polardb20170801) {
    const result = await client.describeDBClusterPerformance(describeDBClusterPerformanceRequest).then(res => res.body);
    for (const item of result.performanceKeys.performanceItem) {
      for (const itemValue of item.points.performanceItemValue) {
        const v = new PluginAliyunMonitorPolardbMetric();
        v.id = `${instanceInfo.instanceId}${item.DBNodeId}${item.metricName}${itemValue.timestamp}`;
        v.instanceId = instanceInfo.instanceId;
        v.instanceName = instanceInfo.instanceName;
        v.nodeId = item.DBNodeId;
        v.type = item.metricName;
        v.t = itemValue.timestamp;
        v.average = Number(itemValue.value);
        await this.pluginAliyunMonitorPolardbQpsRepository.save(v);
      }
    }
  }

}